package edu.ccut.saturn.studio.core.util.eclipse;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.wst.sse.ui.StructuredTextEditor;

public class SaturnXMLFormEditor extends FormEditor {

	private StructuredTextEditor textEditor;
	
	private PropertyListener propertyListener;
	
	private int sourcePageIndex;

	protected void addPages() {
		SaturnProjectUtil.refreshProject(getEditorInput());
		
		try {
			createSourcePage();
			addSourcePage();
			initModel();
			addFormPages();
			updateTitle();
		} catch (PartInitException e) {
			SaturnDialogUtil.openError(super.getSite().getShell(),
					"Create Editor Failed! Because :" + e.getMessage());
		}
	}
	
	protected void initModel() {
	}
	
	protected void addFormPages() throws PartInitException {
	}
	
	private void updateTitle() {
		IEditorInput input = getEditorInput();
		setPartName(input.getName());
		setTitleToolTip(input.getToolTipText());
	}

	protected void createSourcePage() throws PartInitException {
		textEditor = new StructuredTextEditor();
		textEditor.setEditorPart(this);

		if (propertyListener == null) {
			propertyListener = new PropertyListener();
		}

		textEditor.addPropertyListener(propertyListener);
	}

	protected StructuredTextEditor getTextEditor() {
		return textEditor;
	}

	@SuppressWarnings("restriction")
	private void addSourcePage() throws PartInitException {
		try {
			sourcePageIndex = addPage(textEditor, getEditorInput());
			setPageText(
					sourcePageIndex,
					org.eclipse.wst.xml.ui.internal.tabletree.XMLEditorMessages.XMLMultiPageEditorPart_0);
			textEditor.update();

			firePropertyChange(PROP_TITLE);
		} catch (PartInitException exception) {
			// dispose editor
			dispose();
			throw exception;
		}
	}

	public void doSave(IProgressMonitor monitor) {
		textEditor.doSave(monitor);
	}

	public void doSaveAs() {
		textEditor.doSaveAs();
	}

	public boolean isSaveAsAllowed() {
		return textEditor.isSaveAsAllowed();
	}

	@SuppressWarnings("unchecked")
	public Object getAdapter(Class key) {
		Object result = null;

		if (key.equals(IGotoMarker.class)) {
			result = new IGotoMarker() {
				public void gotoMarker(IMarker marker) {
					setActivePage(sourcePageIndex);
					IDE.gotoMarker(textEditor, marker);
				}
			};
		} else {
			if (textEditor != null) {
				result = textEditor.getAdapter(key);
			}
		}

		if (result == null) {
			return super.getAdapter(key);
		} else {
			return result;
		}
	}

	/**
	 * Internal IPropertyListener
	 */
	class PropertyListener implements IPropertyListener {
		/**
		 * Posts the update code "behind" the running operation.
		 */
		private void postOnDisplayQue(Runnable runnable) {
			IWorkbench workbench = PlatformUI.getWorkbench();
			IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
			if (windows != null && windows.length > 0) {
				Display display = windows[0].getShell().getDisplay();
				display.asyncExec(runnable);
			} else
				runnable.run();
		}

		public void propertyChanged(Object source, int propId) {
			switch (propId) {
			// had to implement input changed "listener" so that
			// StructuredTextEditor could tell it containing editor that
			// the input has change, when a 'resource moved' event is
			// found.
			case IEditorPart.PROP_INPUT:
			case IEditorPart.PROP_DIRTY: {
				if (source == getTextEditor()) {
					if (getTextEditor().getEditorInput() != getEditorInput()) {
						setInput(getTextEditor().getEditorInput());
						/*
						 * title should always change when input changes. create
						 * runnable for following post call
						 */
						Runnable runnable = new Runnable() {
							public void run() {
								firePropertyChange(IWorkbenchPart.PROP_TITLE);
							}
						};
						/*
						 * Update is just to post things on the display queue
						 * (thread). We have to do this to get the dirty
						 * property to get updated after other things on the
						 * queue are executed.
						 */
						postOnDisplayQue(runnable);
					}
				}
				break;
			}
			case IWorkbenchPart.PROP_TITLE: {
				// update the input if the title is changed
				if (source == getTextEditor()) {
					if (getTextEditor().getEditorInput() != getEditorInput()) {
						setInput(getTextEditor().getEditorInput());
					}
				}
				break;
			}
			default: {
				// propagate changes. Is this needed? Answer: Yes.
				if (source == getTextEditor()) {
					firePropertyChange(propId);
				}
				break;
			}
			}
		}
	}
}
